﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;

namespace PageAdmin.Utils
{

    /// <summary>
    /// ftp操作有关的方法
    /// </summary>
    public class FtpHelper
    {
        /// <summary>
        /// FTP服务器域名或ip
        /// </summary>
        private string ftpServer { get; set; }
        /// <summary>
        /// FTP服务器默认目录
        /// </summary>
        private string ftpRootPath { get; set; }  //虚拟主机一般是wwwroot
        /// <summary>
        /// FTP服务器登录用户名
        /// </summary>
        private string ftpUser { get; set; }
        /// <summary>
        /// FTP服务器登录密码
        /// </summary>
        private string ftpPassword { get; set; }

        /// <summary>
        /// 默认缓冲区大小，默认8K，太大了占内存，太小了速度慢
        /// </summary>
        private int defaultBufferSize= 8 * 1024;

        /// <summary>
        /// 使用被动模式
        /// </summary>
        private bool usePassive { get; set; } = true;

        /// <summary>
        /// 超时间时间，0则不限制，单位秒
        /// </summary>
        private int timeOut { get; set; } = 0;

        public FtpHelper(string ftpServer, string ftpUser, string password, string rootPath = "", bool UsePassive = true, int timeOut = 0)
        {
            this.ftpServer = ftpServer;
            this.ftpUser = ftpUser;
            this.ftpPassword = password;
            this.usePassive = UsePassive;
            this.timeOut = timeOut;
            this.ftpRootPath = StringHelper.Combine("ftp://" + ftpServer, rootPath);
        }


        /// <summary>
        /// 析构函数回收非托管资源
        /// </summary>
        ~FtpHelper()
        {

        }


        /// <summary>       
        /// 建立FTP链接,返回请求对象       
        /// </summary>      
        /// <param name="uri">FTP地址</param>       
        /// <param name="ftpMethod">操作命令</param>       
        private FtpWebRequest OpenRequest(Uri uri, string ftpMethod)
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(uri);
            if (this.timeOut > 0)
            {
                request.Timeout = this.timeOut * 1000;
            }
            request.Proxy = null;
            request.Method = ftpMethod;
            request.UsePassive = this.usePassive;
            request.UseBinary = true;
            //request.KeepAlive = false;
            request.Credentials = new NetworkCredential(this.ftpUser, this.ftpPassword);
            return request;
        }

        /// <summary>
        /// 建立FTP链接,返回响应对象
        /// </summary>
        /// <param name="uri">FTP地址</param>
        /// <param name="ftpMethod">操作命令</param>
        /// <returns></returns>
        private FtpWebResponse GetResponse(Uri uri, string ftpMethod)
        {
            FtpWebRequest request = OpenRequest(uri, ftpMethod);
            return (FtpWebResponse)request.GetResponse();
        }

        /// <summary>
        /// 创建目录
        /// </summary>
        /// <param name="directory">创建目录</param>
        public void CreateDirectory(string directory)
        {
            directory = directory.Replace("\\", "/");
            string[] directoryArray = directory.Split('/');
            string parentDirPath = "";
            //需要从第一级开始创建。
            foreach(string item in directoryArray)
            {
                if (string.IsNullOrEmpty(item))
                {
                    continue;
                }
                CreateCurrentDirectory(parentDirPath, item);
                parentDirPath=StringHelper.Combine(parentDirPath, item);
            }
        }


        /// <summary>
        /// 在当前目录下创建目录
        /// </summary>
        /// <param name="directory"></param>
        private void CreateCurrentDirectory(string parentDirectoryPath,string directoryName)
        {
            if (IsDirectoryExist(parentDirectoryPath, directoryName))
            {
               return;
            }
            string ftpURI = StringHelper.Combine(this.ftpRootPath, parentDirectoryPath, directoryName);
            using (var response = GetResponse(new Uri(ftpURI), WebRequestMethods.Ftp.MakeDirectory))
            {
            }
        }

        /// <summary>
        /// 更改目录名
        /// </summary>
        /// <param name="currentName">当前名称</param>
        /// <param name="newName">修改后新名称</param>
        public void ReNameDirectory(string currentName, string newName)
        {
            string ftpURI = StringHelper.Combine(this.ftpRootPath, currentName);
            var request = OpenRequest(new Uri(ftpURI), WebRequestMethods.Ftp.Rename);
            request.RenameTo = newName;
            using (var response = (FtpWebResponse)request.GetResponse())
            {
            }
        }

        /// <summary>
        /// 更改文件名
        /// </summary>
        /// <param name="currentName">当前名称</param>
        /// <param name="newName">修改后新名称</param>
        public void ReNameFile(string currentName, string newName)
        {
            string ftpURI = StringHelper.Combine(this.ftpRootPath, currentName);
            if (ftpURI.EndsWith("/") || ftpURI.EndsWith("\\"))
            {
                ftpURI = ftpURI.Substring(0, ftpURI.Length - 1);
            }
            var request = OpenRequest(new Uri(ftpURI), WebRequestMethods.Ftp.Rename);
            request.RenameTo = newName;
            using (var response = (FtpWebResponse)request.GetResponse())
            {
            }
        }


        /// <summary>
        /// 本地文件推送到ftp服务器
        /// </summary>
        /// <param name="savePath">ftp保存路径</param>
        /// <param name="localFile">本地文件路径</param>
        /// <param name="delLocalFile">同步完成是否删除本地文件</param>
        public void Push(string savePath, string localFile, bool delLocalFile = true)
        {
            if (string.IsNullOrEmpty(savePath))
            {
                return;
            }
            string localPath = IOHelper.MapPath(localFile);
            FileInfo fileInf = new FileInfo(localPath);
            if (fileInf == null)
            {
                return;
            }
            var saveDirectory = savePath.Replace(Path.GetFileName(savePath), "");
            CreateDirectory(saveDirectory);
            string ftpURI = StringHelper.Combine(this.ftpRootPath, savePath);
            var request = OpenRequest(new Uri(ftpURI), WebRequestMethods.Ftp.UploadFile);
            request.ContentLength = fileInf.Length;
            byte[] buff = new byte[defaultBufferSize];
            int contentLen;
            using (var fs = fileInf.OpenRead())
            {
                using (var ftpStream = request.GetRequestStream())
                {
                    contentLen = fs.Read(buff, 0, defaultBufferSize);
                    while (contentLen != 0)
                    {
                        ftpStream.Write(buff, 0, contentLen);
                        ftpStream.Flush();
                        contentLen = fs.Read(buff, 0, defaultBufferSize);
                    }
                }
            }
            if (delLocalFile)
            {
                IOHelper.DelFile(localPath);
            }
        }

        /// <summary>
        /// 本地流上传到ftp服务器
        /// </summary>
        /// <param name="remoteFile"></param>
        /// <param name="stream"></param>
        public void Upload(string savePath, Stream stream)
        {
            if (string.IsNullOrEmpty(savePath))
            {
                return;
            }
            var saveDirectory = savePath.Replace(Path.GetFileName(savePath), "");
            CreateDirectory(saveDirectory);
            string ftpURI = StringHelper.Combine(this.ftpRootPath, savePath);
            var request = OpenRequest(new Uri(ftpURI), WebRequestMethods.Ftp.UploadFile);
            request.ContentLength = stream.Length;
            byte[] buff = new byte[defaultBufferSize];
            int contentLen;
            using (stream)
            {
                using (var ftpStream = request.GetRequestStream())
                {
                    contentLen = stream.Read(buff, 0, defaultBufferSize);
                    while (contentLen != 0)
                    {
                        ftpStream.Write(buff, 0, contentLen);
                        ftpStream.Flush();
                        contentLen = stream.Read(buff, 0, defaultBufferSize);
                    }
                }
            }
            request.Abort();
        }

        /// <summary>  
        /// 删除文件  
        /// </summary>  
        /// <param name="fileName">要删除的文件名</param>
        public void DeleteFile(string fileName)
        {
            if (fileName.EndsWith("/") || fileName.EndsWith("\\"))
            {
                fileName = fileName.Substring(0, fileName.Length - 1);
            }
            string ftpURI = StringHelper.Combine(this.ftpRootPath, fileName);
            using (var response = GetResponse(new Uri(ftpURI), WebRequestMethods.Ftp.DeleteFile))
            {

            }
        }

        /// <summary>
        /// 删除目录(包括下面所有子目录和子文件)
        /// </summary>
        /// <param name="directory">要删除的带路径目录名：如web/test</param>
        /*
         * 例：删除test目录
         FTPHelper helper = new FTPHelper("x.x.x.x", "web", "user", "password");                  
         helper.RemoveDirectory("web/test");
         */
        public void DeleteDirectory(string directory)
        {
            var listAll = List(directory);
            foreach (var m in listAll)
            {
                string path = StringHelper.Combine(m.Path);
                if (m.IsDirectory)
                    DeleteDirectory(path);
                else
                    DeleteFile(path);
            };
            string ftpURI = StringHelper.Combine(this.ftpRootPath, directory);
            using (var response = GetResponse(new Uri(ftpURI), WebRequestMethods.Ftp.RemoveDirectory))
            {
            }
        }
        /// <summary>
        /// 获取当前目录的文件和子目录信息
        /// </summary>
        /// <returns></returns>
        public List<FtpFileStruct> List(string directory)
        {
            string ftpURI = StringHelper.Combine(this.ftpRootPath, directory);
            var fileList = new List<FtpFileStruct>();
            using (var response = GetResponse(new Uri(ftpURI), WebRequestMethods.Ftp.ListDirectoryDetails))
            {
                string name = null;
                using (var stream = response.GetResponseStream())
                {
                    using (var sr = new StreamReader(stream))
                    {
                        string line = null;
                        while ((line = sr.ReadLine()) != null)
                        {
                            string[] arrs = line.Split(' ');
                            name = arrs[arrs.Length - 1];
                            var model = new FtpFileStruct()
                            {
                                Name = name,
                                IsDirectory = false,
                                Path = StringHelper.Combine(directory, name)
                            };
                            if (arrs.Contains("<DIR>", StringComparer.OrdinalIgnoreCase))
                            {
                                model.IsDirectory = true;
                            }
                            else if (line.StartsWith("d"))
                            {
                                model.IsDirectory = true;
                            }
                            fileList.Add(model);
                        }
                    }
                }
                return fileList;
            }
        }

        /// <summary>       
        /// 列出当前目录的所有文件       
        /// </summary>       
        public List<FtpFileStruct> FileList(string directory)
        {
            var listAll = List(directory);
            var listFile = listAll.Where(m => m.IsDirectory == false).ToList();
            return listFile;
        }
        /// <summary>       
        /// 列出当前目录的所有一级子目录       
        /// </summary>       
        public List<FtpFileStruct> DirectoryList(string directory)
        {
            var listAll = List(directory);
            var listFile = listAll.Where(m => m.IsDirectory == true).ToList();
            return listFile;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parentDirectory"></param>
        /// <param name="currentName"></param>
        /// <returns></returns>
        private bool IsDirectoryExist(string parentDirectoryPath,string currentName)
        {
            bool isExists = false;
            try
            {
                isExists = DirectoryList(parentDirectoryPath).Any(c => c.Name.Equals(currentName, StringComparison.OrdinalIgnoreCase));
                ////string ftpURI = StringHelper.Combine(this.ftpRootPath, directory);
                ////using (var response = GetResponse(new Uri(ftpURI), WebRequestMethods.Ftp.ListDirectory))
                ////{
                //isExists = true;
                ////}
            }
            catch (Exception ex)
            {
            }
            return isExists;
        }

        /// <summary>       
        /// 判断当前目录下指定的子文件是否存在      
        /// </summary>       
        /// <param name="filePath">远程文件名</param>       
        public bool IsFileExist(string filePath)
        {
            bool isExists = false;
            try
            {
                string ftpURI = StringHelper.Combine(this.ftpRootPath,filePath);
                if (ftpURI.EndsWith("/") || ftpURI.EndsWith("\\"))
                {
                    ftpURI = ftpURI.Substring(0, ftpURI.Length - 1);
                }
                using (var response = GetResponse(new Uri(ftpURI), WebRequestMethods.Ftp.GetFileSize))
                {
                    isExists = true;
                }
            }
            catch(Exception ex)
            {
            }
            return isExists;
        }

    }

    /// <summary>
    /// 文件结构类
    /// </summary>
    public class FtpFileStruct
    {
        /// <summary>
        /// 是否为目录
        /// </summary>
        public bool IsDirectory { get; set; }
        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime CreateTime { get; set; }
        /// <summary>
        /// 文件或目录名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 路径
        /// </summary>
        public string Path { get; set; }
    }
}
